package com.example.analysis.service.impl;

import com.example.analysis.dto.StuScoreDto;
import com.example.analysis.entity.ElectionList;
import com.example.analysis.entity.Course;
import com.example.analysis.entity.ElectionList;
import com.example.analysis.entity.Teacher;
import com.example.analysis.entity.TeacherCourseClass;
import com.example.analysis.entity.*;
import com.example.analysis.entity.Class;
import com.example.analysis.mapper.ElectionListMapper;
import com.example.analysis.service.ElectionListService;
import com.example.analysis.utils.ComCalcUtils;
import com.example.analysis.utils.DifferenceAnalysis;
import com.example.analysis.utils.NormalityTest;
import com.example.analysis.utils.Response;
import org.apache.commons.math3.distribution.NormalDistribution;
import org.apache.commons.math3.stat.inference.KolmogorovSmirnovTest;
import com.example.analysis.vo.ItemVO;
import com.example.analysis.vo.ResultVO;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

@Service
public class ElectionListServiceImpl implements ElectionListService {
    @Resource
    private ElectionListMapper mapper;

    @Override
    public List<ResultVO> normalityTest(String courseId, String teacherId, String semester) {
        List<ElectionList> list = mapper.select(courseId, null, teacherId, semester);
        System.out.println("数据量：" + list.size());
        if (list.isEmpty())
            return null;
        // 期末成绩
        double[] scores = getScore(list);
        ResultVO scoreVO = getResultVO(scores, "期末成绩");
        // 综合成绩
        double[] totalScore = getTotalScore(list);
        ResultVO totalScoreVO = getResultVO(totalScore, "综合成绩");
        List<ResultVO> resultVOList = new ArrayList<>();
        resultVOList.add(scoreVO);
        resultVOList.add(totalScoreVO);
        return resultVOList;
    }

    @Override
    public List<ResultVO> discriminationAnalysis(String courseId, String teacherId, double quantile, String semester) {
        List<ElectionList> list = mapper.select(courseId, null, teacherId, semester);
        System.out.println("数据量：" + list.size());
        if (list.isEmpty())
            return null;
        // 期末成绩
        double[] data = getScore(list);
        ResultVO scoreVO = getResultVODis(data, "期末成绩", quantile);
        // 综合成绩
        double[] totalScore = getTotalScore(list);
        ResultVO tVO = getResultVODis(totalScore, "综合成绩", quantile);
        List<ResultVO> resultVOList = new ArrayList<>();
        resultVOList.add(scoreVO);
        resultVOList.add(tVO);
        return resultVOList;
    }

    @Override
    public List<ResultVO> courseGradeAnalysis(String courseId, String teacherId, String semester) {
        List<StuScoreDto> stuScoreDtoList = mapper.selectScores(courseId, teacherId, semester);
        // 1. 主要年级
        String grade = findMainGrade(stuScoreDtoList);
        // 2. 过滤掉非主要年级，并且转换成每班为一组的数据
        List<Map<String, List<Double>>> maps = genGroups(stuScoreDtoList, grade);
        Map<String, List<Double>> scoreMap = maps.get(0);  // 期末成绩
        Map<String, List<Double>> overallScoreMap = maps.get(1); // 综合成绩
        // 3. 获取每组的数据
        List<double[]> scoreList = new ArrayList<>();
        List<double[]> totalScoreList = new ArrayList<>();
        List<ItemVO> itemVOList1 = new ArrayList<>();
        List<ItemVO> itemVOList2 = new ArrayList<>();
        int cnt = 0;
        for (String key : scoreMap.keySet()) {
            // 获取成绩数据，List<Double> -> double []
            double[] scores = scoreMap.get(key).stream()
                    .mapToDouble(Double::doubleValue)
                    .toArray();
            double[] overallScores = overallScoreMap.get(key).stream()
                    .mapToDouble(Double::doubleValue)
                    .toArray();
            // 将数据放到一起
            scoreList.add(scores);
            totalScoreList.add(overallScores);

            cnt += scores.length;
            // 封装每组的数据
            itemVOList1.add(new ItemVO(0, 0, scores.length, scores, key));
            itemVOList2.add(new ItemVO(0, 0, overallScores.length, overallScores, key));
        }
        System.out.println("数据量：" + cnt + ", 组数：" + scoreMap.size());
        // 组数 < 2或者数据量为0，直接返回
        if (cnt == 0 || scoreList.size() < 2)
            return null;
        // 2. 分析并封装
        List<ResultVO> r = new ArrayList<>();
        double[] double1, double2;
        if (scoreMap.size() == 2) {
            // 2.1 只有2组
            double1 = DifferenceAnalysis.twoGroupAnalysis(scoreList);
            double2 = DifferenceAnalysis.twoGroupAnalysis(totalScoreList);
        } else {
            // 2.2 多组
            double1 = DifferenceAnalysis.moreGroupAnalysis(scoreList);
            double2 = DifferenceAnalysis.moreGroupAnalysis(totalScoreList);
        }


        r.add(new ResultVO(double1[0], double1[1], itemVOList1, "期末成绩"));
        r.add(new ResultVO(double2[0], double2[1], itemVOList2, "综合成绩"));

        if (double1[1] < 0.05)
            System.out.println("期末-差异性显著");
        else
            System.out.println("期末-差异性不显著");

        if (double2[1] < 0.05)
            System.out.println("综合-差异性显著");
        else
            System.out.println("综合-差异性不显著");
        return r;
    }

    @Override
    public List<Course> getCourses() {
        return mapper.selectCourses();
    }

    @Override
    public List<Teacher> getTeachers(String courseId) {
        return mapper.selectTeacher(courseId);
    }

    @Override
    public List<String> getSemesters(String courseId, String teacherId) {
        return mapper.selectSemesters(courseId, teacherId);
    }

    // 获取期末成绩
    private double[] getScore(List<ElectionList> list) {
        int n = list.size();
        double[] scores = new double[n];
        for (int i = 0; i < n; i++) {
            scores[i] = list.get(i).getScore();
        }
        return scores;
    }

    // 综合成绩
    private double[] getTotalScore(List<ElectionList> list) {
        int n = list.size();
        double[] scores = new double[n];
        for (int i = 0; i < n; i++) {
            ElectionList electionList = list.get(i);
            int sum = electionList.getProScore() + electionList.getProDay() + electionList.getProEx() + electionList.getProHalf();
            scores[i] = (electionList.getScore() * electionList.getProScore() +
                    electionList.getDayScore() * electionList.getProDay() +
                    electionList.getExperScore() * electionList.getExperScore() +
                    electionList.getHalfScore() * electionList.getProHalf()) / sum;
        }
        return scores;
    }

    // 正态性检验：封装ResultVO
    private ResultVO getResultVO(double[] scores, String type) {
        double[] res = NormalityTest.ksTest(scores);
        double pValue = res[3];
        if (pValue > NormalityTest.alpha)
            System.out.println("正态");
        else
            System.out.println("非正态");

        ItemVO itemVO = new ItemVO(res[0], res[1], scores.length, scores, "");
        List<ItemVO> r = new ArrayList<>();
        r.add(itemVO);
        return new ResultVO(res[2], res[3], r, type);
    }

    // 区分度分析：封装ResultVO
    private ResultVO getResultVODis(double[] data, String info, double quantile) {
        // 分组
        List<double[]> groups = DifferenceAnalysis.divideGroups(data, quantile);
        // 封装分组
        List<ItemVO> itemVOList = new ArrayList<>();
        for (double[] i : groups) {
            double mean = ComCalcUtils.calcMean(i);
            double std = ComCalcUtils.calcStd(i, mean);
            ItemVO itemVO = new ItemVO(mean, std, i.length, i, "");
            itemVOList.add(itemVO);
        }
        itemVOList.get(0).setInfo("高分组");
        itemVOList.get(1).setInfo("低分组");
        // 区分度分析
        double[] res = DifferenceAnalysis.twoGroupAnalysis(groups);
        if (res[1] < 0.05) {
            System.out.println("区分显著");
        } else {
            System.out.println("区分不显著");
        }
        return new ResultVO(res[0], res[1], itemVOList, info);
    }

    /**
     * 查询主要的年级，排除重修的成绩
     *
     * @param stuScoreDtoList 成绩
     * @return 主要年级
     */
    private String findMainGrade(List<StuScoreDto> stuScoreDtoList) {
        // 1. 统计
        Map<String, Integer> hash = new HashMap<>();
        for (StuScoreDto dto : stuScoreDtoList) {
            String studentId = dto.getStudentId();
            String grade = studentId.substring(2, 4); // 从学号中获取年级
            // 存储到hash表中
            if (hash.containsKey(grade)) {
                hash.put(grade, hash.get(grade) + 1);
            } else {
                hash.put(grade, 1);
            }
        }
        // 2. 遍历hash找到主要的年级
        Set<Map.Entry<String, Integer>> entries = hash.entrySet();
        int maxNum = 0;
        String key = null;
        for (Map.Entry<String, Integer> entry : entries) {
            maxNum = maxNum < entry.getValue() ? entry.getValue() : maxNum;
            key = entry.getKey();
        }
        return key;
    }

    /**
     * 获取每组数据
     *
     * @param stuScoreDtoList 成绩
     * @param grade           主要年级
     * @return 组数据
     */
    private List<Map<String, List<Double>>> genGroups(List<StuScoreDto> stuScoreDtoList, String grade) {
        Map<String, List<Double>> scoreMap = new HashMap<>();
        Map<String, List<Double>> overallScoreMap = new HashMap<>();
        for (StuScoreDto dto : stuScoreDtoList) {
            String studentId = dto.getStudentId();
            // 1. 过滤掉其他年级的数据
            if (!grade.equals(studentId.substring(2, 4))) {
                continue;
            }
            // 2. 归类
            String className = dto.getClassName();
            //   2.1 期末成绩
            System.out.println(scoreMap.get(className));
            if (!scoreMap.containsKey(className)) {
                List<Double> scoreList = new ArrayList<>();
                scoreList.add(dto.getScore());
                scoreMap.put(className, scoreList);
            } else {
                List<Double> scoreList = scoreMap.get(className);
                scoreList.add(dto.getScore());
            }
            System.out.println(scoreMap.get(className));
            //   2.2 综合成绩
            int sum = dto.getProScore() + dto.getProDay() + dto.getProEx() + dto.getProHalf();
            double overallScore = (dto.getScore() * dto.getProScore() +
                    dto.getDayScore() * dto.getProDay() +
                    dto.getExperScore() * dto.getExperScore() +
                    dto.getHalfScore() * dto.getProHalf()) / sum;
            if (!overallScoreMap.containsKey(className)) {
                List<Double> scoreList = new ArrayList<>();
                scoreList.add(overallScore);
                overallScoreMap.put(className, scoreList);
            } else {
                List<Double> scoreList = overallScoreMap.get(className);
                scoreList.add(overallScore);
            }
        }
        List<Map<String, List<Double>>> ret = new ArrayList<>();
        ret.add(scoreMap);
        ret.add(overallScoreMap);
        return ret;
    }

    private double[] getExperimentScore(List<Election> list) {
        int n = list.size();
        double[] scores = new double[n];
        for (int i = 0; i < n; i++) {
            scores[i] = list.get(i).getExper_score();
        }
        return scores;
    }

/*
    void normalityTestByAPI() {
        List<ElectionList> list = mapper.select();
        for (ElectionList item : list) {
            System.out.println(item);
        }
        int n = list.size();
        double[] scores = new double[n];
        for (int i = 0; i < n; i++) {
            scores[i] = list.get(i).getScore();
        }

        double mean = NormalityTest.calcMean(scores);
        double stdDev = NormalityTest.calcStd(scores, mean);
        NormalDistribution normalDist = new NormalDistribution(mean, stdDev);
        KolmogorovSmirnovTest ksTest = new KolmogorovSmirnovTest();
        double ksStatistic = ksTest.kolmogorovSmirnovStatistic(normalDist, scores);
        double pValue1 = ksTest.kolmogorovSmirnovTest(normalDist, scores, true);
        System.out.println("----- API实现 ------");
        System.out.println("KS统计量: " + ksStatistic);
        System.out.println("P值: " + pValue1);
        System.out.println("拒绝原假设（α=0.05）? " + (pValue1 < 0.05));
    }
*/

}
